#include "database_query_callback.h"

#include "report.h"
#include "duration.h"

namespace afcore {
namespace database {

/// @brief  模板构造 原位构造
/// @tparam T       要构造的对象类型
/// @tparam ...Args 构造参数
/// @param  t       构造的对象
/// @param  ...args 构造参数
template<typename T, typename... Args>
inline void Construct(T& t, Args&&... args)
{
  new (&t) T(std::forward<Args>(args)...);
}

/// @brief  模板析构
/// @tparam T       要析构的对象类型
/// @param  t       要析构的对象
template<typename T>
inline void Destroy(T& t)
{
  t.~T();
}

/// @brief  构造活跃成员
/// @tparam T       要构造成员的对象自身类型
/// @param  obj     要构造成员的对象
template<typename T>
inline void ConstructActiveMember(T* obj)
{
  if (!obj->is_prepared_)
    Construct(obj->string_);
  else
    Construct(obj->prepared_);
}

/// @brief  析构活跃成员
/// @tparam T       要析构成员的对象自身类型
/// @param  obj     要析构成员的对象
template<typename T>
inline void DestroyActiveMember(T* obj)
{
  if (!obj->is_prepared_)
    Destroy(obj->string_);
  else
    Destroy(obj->prepared_);
}

/// @brief  移动对象成员
/// @tparam T       要移动的对象类型
/// @param  to      要移动的目的地
/// @param  from    要移动的来源
template<typename T>
inline void MoveFrom(T* to, T&& from)
{
  DBG_ASSERT(to->is_prepared_ == from.is_prepared_);

  if (!to->is_prepared_)
    to->string_ = std::move(from.string_);
  else
    to->prepared_ = std::move(from.prepared_);
}

/// @brief  回调数据
struct CQueryCallback::SQueryCallbackData
  : public CNocopyable {
public:
  friend class CQueryCallback;

  /// @brief  构造函数
  /// @param  callback    原始查询结果回调函数
  explicit SQueryCallbackData(std::function<void(CQueryCallback&, RQueryResultSptr)>&& callback)
    : string_(std::move(callback))
    , is_prepared_(false) {
  }

  /// @brief  构造函数
  /// @param  callback    准备查询结果回调函数
  explicit SQueryCallbackData(std::function<void(CQueryCallback&, RPreparedQueryResultSptr)>&& callback)
    : prepared_(std::move(callback))
    , is_prepared_(true) {
  }

  /// @brief  移动构造函数
  /// @param  that        要移动的查询数据
  SQueryCallbackData(SQueryCallbackData&& that) noexcept {
    is_prepared_ = that.is_prepared_;
    ConstructActiveMember(this);
    MoveFrom(this, std::move(that));
  }

  /// @brief  移动赋值函数
  /// @param  that        要移动赋值的查询数据
  /// @return 构造好的查询数据
  /// @note   union类型不一致的时候，需要先释放之前的查询结果
  SQueryCallbackData& operator=(SQueryCallbackData&& that) noexcept {
    if (this != &that) {
      if (is_prepared_ != that.is_prepared_) {
        DestroyActiveMember(this);
        is_prepared_ = that.is_prepared_;
        ConstructActiveMember(this);
      }
      MoveFrom(this, std::move(that));
    }
    return *this;
  }

  /// @brief  析构函数
  ~SQueryCallbackData() {
    DestroyActiveMember(this);
  }

private:
  template<typename T> friend void ConstructActiveMember(T* obj);
  template<typename T> friend void DestroyActiveMember(T* obj);
  template<typename T> friend void MoveFrom(T* to, T&& from);
private:
  union {
    std::function<void(CQueryCallback&, RQueryResultSptr)> string_;           ///< 原始查询回调
    std::function<void(CQueryCallback&, RPreparedQueryResultSptr)> prepared_; ///< 准备查询回调
  };
  bool is_prepared_ {false}; ///< 准备标识
};

CQueryCallback::CQueryCallback(RQueryResultFutrue&& result) {
  is_prepared_ = false;
  Construct(string_, std::move(result));
}

CQueryCallback::CQueryCallback(RPreparedQueryResultFutrue && result) {
  is_prepared_ = true;
  Construct(prepared_, std::move(result));
}

CQueryCallback::CQueryCallback(CQueryCallback&& that) noexcept {
  is_prepared_ = that.is_prepared_;
  ConstructActiveMember(this);
  callbacks_ = std::move(that.callbacks_);
  MoveFrom(this, std::move(that));
}

CQueryCallback &CQueryCallback::operator=(CQueryCallback&& that) noexcept {
  if (this != &that) {
    if (is_prepared_ != that.is_prepared_) {
      DestroyActiveMember(this);
      is_prepared_ = that.is_prepared_;
      ConstructActiveMember(this);
    }
    callbacks_ = std::move(that.callbacks_);
    MoveFrom(this, std::move(that));
  }
  return *this;
}

CQueryCallback::~CQueryCallback() {
  DestroyActiveMember(this);
}

CQueryCallback&& CQueryCallback::WithCallback(std::function<void(RQueryResultSptr)>&& callback) {
  return WithChainingCallback(
    [callback](CQueryCallback& /*this*/, RQueryResultSptr result) {
      callback(std::move(result));
    });
}

CQueryCallback&& CQueryCallback::WithPreparedCallback(std::function<void(RPreparedQueryResultSptr)>&& callback) {
  return WithChainingPreparedCallback(
    [callback](CQueryCallback& /*this*/, RPreparedQueryResultSptr result) {
      callback(std::move(result));
    });
}

CQueryCallback&& CQueryCallback::WithChainingCallback(std::function<void(CQueryCallback&, RQueryResultSptr)>&& callback) {
  DBG_ASSERT(!callbacks_.empty() ||!is_prepared_, "Attempted to set callback function for string query on a prepared async query");
  callbacks_.emplace(std::move(callback));
  return std::move(*this);
}

CQueryCallback&& CQueryCallback::WithChainingPreparedCallback(std::function<void(CQueryCallback&, RPreparedQueryResultSptr)>&& callback) {
  DBG_ASSERT(!callbacks_.empty() ||is_prepared_, "Attempted to set callback function for prepared query on a string async query");
  callbacks_.emplace(std::move(callback));
  return std::move(*this);
}

void CQueryCallback::SetNextQuery(CQueryCallback &&next) {
  MoveFrom(this, std::move(next));
}

CQueryCallback::EStatus CQueryCallback::InvokeIfReady() {
  SQueryCallbackData& callback = callbacks_.front();
  auto CheckStateAndReturnCompletion = [this]() {
    callbacks_.pop();
    bool has_next = !is_prepared_ ? string_.valid() : prepared_.valid();
    if (callbacks_.empty()) {
      DBG_ASSERT(!has_next);
      return kStatus_Completed;
    }

    //! 链式上如果下一个没准备就 就完成 后面的不管了
    if (!has_next) {
      return kStatus_Completed;
    }

    DBG_ASSERT(is_prepared_ == callbacks_.front().is_prepared_);
    return kStatus_NextStep;
  };

  if (!is_prepared_) {
    if (string_.valid() && string_.wait_for(RSeconds(0)) == std::future_status::ready) {
      RQueryResultFutrue f(std::move(string_));
      std::function<void(CQueryCallback&, RQueryResultSptr)> cb(std::move(callback.string_));
      cb(*this, f.get());
      return CheckStateAndReturnCompletion();
    }
  } else {
    if (prepared_.valid() && prepared_.wait_for(RSeconds(0)) == std::future_status::ready) {
      RPreparedQueryResultFutrue f(std::move(prepared_));
      std::function<void(CQueryCallback&, RPreparedQueryResultSptr)> cb(std::move(callback.prepared_));
      cb(*this, f.get());
      return CheckStateAndReturnCompletion();
    }
  }

  return kStatus_NotReady;
}

} // !namespace database
} // !namespace afcore